package com.yubest;

/**
 * 给定一个由整数数组 A 表示的环形数组 C，求 C 的非空子数组的最大可能和。
 * <p>
 * 在此处，环形数组意味着数组的末端将会与开头相连呈环状。（形式上，当0 <= i < A.length 时 C[i] = A[i]，且当 i >= 0 时 C[i+A.length] = C[i]）
 * <p>
 * 此外，子数组最多只能包含固定缓冲区 A 中的每个元素一次。（形式上，对于子数组 C[i], C[i+1], ..., C[j]，不存在 i <= k1, k2 <= j 其中 k1 % A.length = k2 % A.length）
 * <p>
 *  
 * <p>
 * 示例 1：
 * <p>
 * 输入：[1,-2,3,-2]
 * 输出：3
 * 解释：从子数组 [3] 得到最大和 3
 * 示例 2：
 * <p>
 * 输入：[5,-3,5]
 * 输出：10
 * 解释：从子数组 [5,5] 得到最大和 5 + 5 = 10
 * 示例 3：
 * <p>
 * 输入：[3,-1,2,-1]
 * 输出：4
 * 解释：从子数组 [2,-1,3] 得到最大和 2 + (-1) + 3 = 4
 * 示例 4：
 * <p>
 * 输入：[3,-2,2,-3]
 * 输出：3
 * 解释：从子数组 [3] 和 [3,-2,2] 都可以得到最大和 3
 * 示例 5：
 * <p>
 * 输入：[-2,-3,-1]
 * 输出：-1
 * 解释：从子数组 [-1] 得到最大和 -1
 *  
 * <p>
 * 提示：
 * <p>
 * -30000 <= A[i] <= 30000
 * 1 <= A.length <= 30000
 * <p>
 * 来源：力扣（LeetCode）
 * 链接：https://leetcode-cn.com/problems/maximum-sum-circular-subarray
 * 著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。
 *
 * @Author hweiyu
 * @Description
 * @Date 2021/12/27 16:46
 */
public class P0918 {

    public static void main(String[] args) {
//        new Solution918().maxSubarraySumCircular(new int[]{1, -2, 3, -2});
//
//        new Solution918().maxSubarraySumCircular(new int[]{9, -4, -7, 9});
//
//        new Solution918().maxSubarraySumCircular(new int[]{-2, 4, -5, 4, -5, 9, 4});
//
//        new Solution918().maxSubarraySumCircular(new int[]{0, 0, 0, 0, 0});

        new Solution918().maxSubarraySumCircular(new int[]{1, 2, 3, 4, 5, 6});
    }
}

class Solution918 {

//    public int maxSubarraySumCircular(int[] nums) {
//        int max = -30000;
//        //t[0]：最大值，t[1]：开始参与计算的下标
//        int[] t;
//        for (int i = 0; i < nums.length; i++) {
//            t = maxSubarraySumCircular(nums, i);
//            max = Math.max(max, t[0]);
//            if (t[1] < i) {
//                break;
//            } else if (t[1] > i) {
//                i = t[1] - 1;
//            }
//        }
//        return max;
//    }
//
//    private int[] maxSubarraySumCircular(int[] nums, int start) {
//        int[] result = new int[]{-30000, start};
//        int n = nums.length;
//        int pre = 0;
//        for (int i = start; i < start + n; i++) {
//            if (pre + nums[i % n] <= nums[i % n]) {
//                result[1] = i % n;
//            }
//            pre = Math.max(pre + nums[i % n], nums[i % n]);
//            result[0] = Math.max(result[0], pre);
//        }
//        return result;
//    }

    /**
     * 思路：Kadane 算法 + 最小子序和
     * 1、子序和存在两种情况
     *   1.1、存在于 [0, n] 的单区间里
     *   1.2、存在于 [0, i] 和 [j, n] 的双区间中。
     *        假如最大子序和在双区间中，此时首尾两元素必定在所求解的区间上，所以，假如存在最小子序和时，一定在 [1, n - 1] 中
     *        所以，问题转换，即只要计算出总值，然后减去最小区间的值，即可获取到最大区间的和
     * 2、因此可以分别计算 1.1 和 1.2 的最大子序和，比较出最大值即可
     * @param nums
     * @return
     */
    public int maxSubarraySumCircular(int[] nums) {
        int max = -30000, pre = 0, sum = 0;
        //kadane
        for (int num : nums) {
            pre = Math.max(pre + num, num);
            max = Math.max(max, pre);
            sum += num;
        }
        Integer min = minSubarraySumCircular(nums);
        return null == min ? max : Math.max(max, sum - min);
    }

    public Integer minSubarraySumCircular(int[] nums) {
        Integer min = null;
        int pre = 0;
        for (int i = 1; i < nums.length - 1; i++) {
            pre = Math.min(pre + nums[i], nums[i]);
            if (min == null) {
                min = pre;
            } else {
                min = Math.min(min, pre);
            }
        }
        return min;
    }
}
